// #include <chrono>

#include "../support/Timing.hpp"
#include "Externs.hpp"

#include "SWARMTask.hpp"
#include "SWARMTaskTag.hpp"
#include "SWARMTaskTagWrapper.hpp"

template <int TASK_ID, int NPARAMS, int TASKTAGDIM, int INTERTASKDIM> 
void SWARMTask <TASK_ID, NPARAMS, TASKTAGDIM, INTERTASKDIM>::operator()() {  
  PARAMS.insert(PARAMS.begin(), params.begin(), params.end());

  SwarmTaskTag<TASK_ID, TASKTAGDIM, INTERTASKDIM> *t = 
    new SwarmTaskTag<TASK_ID, TASKTAGDIM, INTERTASKDIM>
    (0, 0, 0, static_cast<tag_elem_t>(params[0]), 
     static_cast<tag_elem_t>(params[1]), static_cast<tag_elem_t>(params[2]), 
     static_cast<tag_elem_t>(params[3]));
  SwarmTagWrapper<SwarmTaskTag<TASK_ID, TASKTAGDIM, INTERTASKDIM> > stw(t);

  pthread_mutex_init(&initialization_waiter, NULL);
  pthread_cond_init (&initialization_cv, NULL);
  
  pthread_mutex_init(&final_waiter, NULL);
  pthread_cond_init (&final_cv, NULL);

  tbb::tick_count tick = tbb::tick_count::now();
  // std::chrono::time_point<std::chrono::system_clock> tick = 
  //   std::chrono::system_clock::now();

#ifdef MEASURE_OVERHEADS
  GlobalTimers::Singleton().init();
#endif // MEASURE_OVERHEADS

  // TODO: Isolate the sequential overhead measurement  
  swarm_Runtime_t *runtime = 
    swarm_posix_startRuntime(NULL, &CODELET(startup), &stw, NULL);

  pthread_mutex_lock(&final_waiter);
  while (!finished_heat3d) {
    printf("Wait Exec\n");
    pthread_cond_wait(&final_cv, &final_waiter);
  }
  pthread_mutex_unlock(&final_waiter);

  std::cout << "Total time for init and parallel execution of a stencil " 
            << "of size ";
  if (NPARAMS > 0) {
    assert(PARAMS.size() == NPARAMS);
    std::cout << PARAMS[0];
    for (uint64_t i=1; i<PARAMS.size(); ++i) {
      std::cout << " x " << PARAMS[i];
    }
  }

  std::cout << " is " << (tbb::tick_count::now()-tick).seconds() << " secs\n";
  // std::cout << " is " << 
  //   ((std::chrono::duration_cast<std::chrono::microseconds>
  //    (std::chrono::system_clock::now() - tick).count()) / 1000000.0) << " secs\n";

  swarm_shutdownRuntime(runtime);
}


// template <int TASK_ID, int NPARAMS, int TASKTAGDIM, int INTERTASKDIM> 
// void SWARMTask <TASK_ID, NPARAMS, TASKTAGDIM, INTERTASKDIM>::operator()() {
//   pthread_mutex_init(&initialization_waiter, NULL);
//   pthread_cond_init (&initialization_cv, NULL);

//   // TODO: Add the sequential overhead measurement  
//   swarm_Runtime_t *runtime = 
//     swarm_posix_startRuntime(NULL, &CODELET(initialize_once), NULL, NULL);
  
//   pthread_mutex_lock(&initialization_waiter);
//   while (!finished_initialization) {
//     std::cout << "Wait Init\n";
//     pthread_cond_wait(&initialization_cv, &initialization_waiter);
//   }
//   pthread_mutex_unlock(&initialization_waiter);
//   std::cout << "Done Init\n";

//   pthread_mutex_init(&final_waiter, NULL);
//   pthread_cond_init (&final_cv, NULL);
//   std::cout << "Dispatch startup\n";

//   std::cout << "Tick\n";
//   tbb::tick_count tick = tbb::tick_count::now();

//   swarm_callInto_result_t result;
//   swarm_callInto_async(runtime, &CODELET(startup), NULL, NULL, &result);
//   assert(result.type == swarm_callInto_RT_RUN);

//   pthread_mutex_lock(&final_waiter);
//   while (!finished_heat3d) {
//     printf("Wait Exec\n");
//     pthread_cond_wait(&final_cv, &final_waiter);
//   }
//   pthread_mutex_unlock(&final_waiter);

//   std::cout << "Total time for init and parallel execution of a problem " 
//             << "of size ";
//   if (NPARAMS > 0) {
//     std::cout << params[0];
//     for (int i=1; i<params.size(); ++i) {
//       std::cout << " x " << params[i];
//     }
//   }
//   std::cout << " is " << (tbb::tick_count::now()-tick).seconds() << " secs\n";

//   result = swarm_callInto(runtime, &CODELET(finalize), NULL, NULL);
//   assert(result.type == swarm_callInto_RT_RUN);

//   swarm_shutdownRuntime(runtime);
// }


#include "explicitInstantiations.cpp.include"
